Mirror uptizm's agent tooling: make the worktree path work, and measure the parts that were only asserted - #12
Conversation
…age skills Mirrored from uptizm, which found both defects while comparing the two repos. `docs/component-registry.md` carried `generated: manual (design:registry planned)` and `last_updated: 2026-06-25`, and what it documented was `magic_starter`'s generic library rather than this repository's. All three components under `lib/ui/components/` appeared nowhere in it. That is the worst shape a registry can take, because AGENTS.md sends a reader there before writing a widget: they conclude the component does not exist and scaffold a second one. `bin/sync-registry` writes it from `lib/ui/components/` and `lib/preview/`, and renders a missing preview or a missing `index.dart` as a bold cell rather than omitting it, so a rule violation appears in a table a reviewer already reads. It discovers components rather than listing them, which matters here more than in a product repo: this is a fork base, and the three components are examples to replace. `bin/sync-skills` copies the `magic-framework` and `wind-ui` skills from the sibling working trees into `.github/skills/`, where a reviewer with only this checkout can read them. Copies rather than symlinks, because the source is a separate repository and a link resolves to nothing on GitHub. Each copy records the sha256 of its source, which lets a checkout without the siblings verify it was not hand-edited even though it cannot check upstream freshness. Only two of the five sibling skills travel: `artisan`, `dusk` and `telescope` describe tools that drive a running app, which a reviewer looking at a diff cannot use.
…t would measure pub.dev AGENTS.md said two mechanisms copy the three gitignored files a worktree needs and that neither covers every path alone. Only one of them was true: `bin/check` copied all three, while `.worktreeinclude` carried `pubspec_overrides.yaml` and nothing else. So a worktree Claude Code created had no `backend/.env` and no `.artisan/plugins.json`, and each of those fails without naming itself: artisan aborts before it says why, and every plugin command disappears from `./bin/fsa`. The root `.env` is deliberately still absent from that list, and the file now says so: it is committed rather than gitignored, because it is a bundled pubspec asset and a missing asset fails `flutter build`. The copier only handles gitignored files, and a tracked one is in the worktree already. `bin/check` gains `require_local_siblings`, which refuses when `pubspec_overrides.yaml` is absent or points at a directory that has moved. Without it the siblings resolve from pub.dev, `flutter pub get` succeeds, and the suite passes against the PUBLISHED packages while the diff under review is of the local ones. Nothing errors; the run just measures something else. It also gains a `registry` job so a stale component registry fails locally rather than only in CI.
`Backend (pint + tests)` is a required check, so a style violation that reaches a push costs a CI round. A PostToolUse hook runs `vendor/bin/pint --test` on each `.php` file Claude writes and returns the failure into the session. It is registered in `.claude/settings.json` rather than `settings.local.json`. The latter is gitignored, a worktree is a fresh checkout, and work in this repo happens in worktrees, so the hook would be silent exactly where it is needed. The cost is that it runs for anyone who clones this template. The script is read-only by construction (`--test` reports rather than rewrites), the executable name is baked in at authoring time and never read from repository content, it resolves both sides of the path comparison before deciding a file is inside the project, and it skips `.git`, any `.env`, `vendor/`, and anything shaped like a key. Proven here rather than assumed: a file pint rejects produces the report, a path outside the project is refused, and `jq -e` confirms the matcher and command nest where the runtime looks for them. It exits silently when `backend/vendor/` is absent, which is the state of a fresh clone rather than a fault, and that is why the first attempt at the proof looked like a broken hook. `worktree.baseRef` is set to `fresh` in the same file, so a new worktree branches from the remote default rather than from a local `main` that may be behind.
… worktree copy AGENTS.md said `.worktreeinclude` and `bin/check` together covered the three gitignored files a worktree needs. They do now; the sentence was written before that was true and is corrected in place rather than deleted, because the shape of the mistake is the useful part: a file listed in one mechanism and not the other reads as covered. The verification section gains the two gates that live only in CI (the `.github` mirrors and the package skill copies), the registry job, and the linting hook with the one state in which it is deliberately silent. The generated-files list gains `docs/component-registry.md` and `.github/skills/`. CI checks both new artifacts in the `Instruction mirrors` job, which needs no toolchain for either: `sync-skills --check` falls back to the recorded hash when the siblings are absent, and `sync-registry --check` is Python over `lib/`.
|
Note Kodizm (AI-generated). May contain mistakes; verify before acting. The two new Major
Nothing in the repo declares PyYAML:
An env escape (
Minor
TestsNothing tests the three new scripts; they are self-verifying through Checks I ran
|
…entences the registry cuts Review findings on this PR, fixed here and in uptizm, where the tooling is authored. The stale-path guard depended on PyYAML and nothing in this repo declares it. `import yaml` sat outside the try, so on a machine without it the ImportError left stdout empty, `missing` came back empty, and the guard PASSED. bin/check carries `set -u -o pipefail` and no `-e`, so the failed command substitution did not stop the run either. The file's shape is fixed and machine-written, so it is parsed with two regexes from the standard library now and the dependency is gone; a parser that falls over is a hard failure rather than a silent pass. bin/check also refused to run at all without pubspec_overrides.yaml, with no way out, and this repo exists to be forked. In a fork there are no sibling working trees and hosted resolution is the correct answer, so CHECK_ALLOW_HOSTED=1 is the opt-in escape and the absent-file message names it. Verified against a real fork rather than a worktree, because bootstrap_ignored_files copies the file into a worktree before the guard ever sees it missing. first_prose_line took only the first line of a doc block, and a doc block wraps at the line length, so all three rows in the registry ended mid-word: "Demonstrates a", "built", "demonstrating a two-axis". It joins the opening paragraph now and cuts at the first sentence, so a physical line carrying two sentences does not drag the whole paragraph into a table cell either. Also from the review: a `|` in a doc line split its row into extra cells and is escaped now, and an empty description rendered as a blank cell rather than a gap and now says so, the same way a missing preview already did.
|
All five findings verified against the source and fixed, here and in uptizm#142. The tooling is authored in uptizm and was already on its The PyYAML no-op. Confirmed exactly as described: No opt-out on a fork base. The second run stops at the next legitimate gate, which is the intended behaviour. The truncated descriptions. Fixed, and the first attempt overshot in a way worth naming: joining the wrapped paragraph and stopping when a physical LINE ends with a terminator pulled whole four-sentence paragraphs into the cells, because one line can carry two sentences. It cuts at the first sentence now. All three rows read as sentences:
The two minors. A On the On tests: still none for the three scripts, and your point stands that the PyYAML gap survived a green run precisely because nothing exercises the failing branch. I exercised all four paths by hand (healthy, two stale paths, absent file, escape) and recorded the output above; a real test for them is worth its own change rather than being folded in here.
|
|
Note Kodizm (AI-generated). May contain mistakes; verify before acting. Incremental re-review of Major
One extra regex for Minor
TestsUnchanged and still none, which you already answered: the four paths were exercised by hand and recorded. The flow-style case above is the one that hand-exercising missed, which is the argument for the follow-up change you named. Checks I ran
|
…he escape
Second review pass, and it caught the previous fix reintroducing the failure it
replaced. The hand-rolled parser only understood block style, so
`magic: {path: /gone}` set no current package, its path was never examined, and
a stale flow-style override passed exactly the way the PyYAML gap did. That is a
narrowing against the PyYAML version, which read both.
The premise in the comment was the wrong part: it claimed the file's shape is
"fixed and machine-written". Nothing generates it. It is written by hand, and
AGENTS.md writes the override in the flow form, so the style the parser did not
read is the style this repo's own documentation teaches.
Both forms are covered now, and a `dependency_overrides` line the parser cannot
classify exits non-zero instead of being skipped, which is what makes the claim
about hard failures actually true. A `git:` override has no path to check, so
its nested keys are still ignored rather than treated as unknown.
Exercised against nine shapes: block-stale, flow-stale, flow with a second key,
flow-healthy, quoted flow, a single-line version followed by a stale entry, a
git override, a second top-level block, and an unsupported list form. Only the
last exits non-zero, and the healthy ones stay silent.
The escape existed only in bin/check's own message. README.md step 5 tells a
fork to delete pubspec_overrides.yaml and AGENTS.md said flatly that bin/check
refuses without it, so a forker following the README hit a wall neither document
predicted. Both name CHECK_ALLOW_HOSTED=1 now.
|
Both fixed, and the major one was a fair hit: the replacement reintroduced the failure it replaced, in a different shape. Flow style. Reproduced before fixing, running the heredoc against two files differing only in style, exactly as you did: The premise in my comment was the wrong part, and that is the useful correction: I wrote that the file's shape is "fixed and machine-written". Nothing generates it, Both forms are read now. I took your second suggestion as well as the first, because the comment claimed something that was not true: a Exercised against nine shapes:
Then end to end through The docs. Correct, and worse than one clause: On tests: you are right that hand-exercising is what missed the flow case, and it would have missed it again. Nine shapes by hand is better than four, and it is still the same instrument. A real test for these three scripts is the follow-up, not something to fold in here.
|
…STED note The instruction-mirror gate is a CI check rather than a bin/check job, so editing AGENTS.md without running bin/sync-instructions goes green locally and red on the PR. That is exactly what happened here.
|
Note Kodizm (AI-generated). May contain mistakes; verify before acting. Incremental re-review of Since the last round: Major
and end to end with a four-space file planted: Requiring the entry key at any consistent indent, or treating a Minor
TestsStill none for these scripts, which you already answered and named as the follow-up. Worth noting the indentation case is the second one hand-exercising missed, both times in the "shapes I did not think of" category that a table-driven test would cover cheaply. Checks I ran
|
… shape Third review round, and the third shape this guard did not read: four-space and tab indentation. The nested branch tested for exactly four spaces, so a four-space entry had its name line consumed as "nested" (setting no current package) and its eight-space path line consumed the same way. Silent pass again, without even reaching the exit-3 branch added last round, because both lines were swallowed before the unknown-line check. The pattern is the finding. PyYAML absent, then flow form, then indentation: every fix was keyed to the shapes somebody had thought of, so every fix found the next one. It walks by INDENT LEVEL now, tracking the open key stack rather than matching fixed-width line shapes. It expands tabs, accepts a quoted key, and strips a trailing comment the same way in both forms, which also closes the two smaller findings: `"magic":` used to exit 3 on a valid file, and `path: /tmp # local` was reported as missing while the flow form with the same comment passed. Exercised against fifteen shapes and then end to end through the gate; only an unsupported list form exits non-zero, and a `git:` override stays silent because its path is a sub-directory one level deeper.
|
All three fixed, and the indentation one is the third round of the same mistake, so I stopped patching shapes. The pattern is the finding. PyYAML absent, then flow form, then indentation: each fix was keyed to the shapes somebody had thought of, so each one found the next. A fourth patch would have been the same mistake a fourth time. You are right that the exit-3 branch did not help here either, because both lines were swallowed as "nested" before the unknown-line check ever ran. So it walks the file by indent level now, tracking the open key stack instead of matching fixed-width line shapes. It expands tabs, accepts a quoted key, and strips a trailing comment identically in both forms. What it looks for is one thing: a That closes both minors as a side effect rather than as separate patches. Fifteen shapes, parser level and then end to end through
The On tests: you have now named the same gap three times and been right three times, so this stops being something I answer and becomes the next change. Hand-exercising fifteen shapes is still hand-exercising, and the shapes I do not think of are precisely the failure mode. A table-driven test over these fifteen is cheap and I will raise it separately rather than growing this PR further.
|
|
Note Kodizm (AI-generated). May contain mistakes; verify before acting. Incremental re-review of Major
and downstream, Minor
TestsUnchanged and still none, which you have now committed to as a separate change - fair. Worth noting the flow- Checks I ran
|
…ow-form git false positive
Fourth review round found the mirror of the bug the third one fixed. The depth
rule keeps a block-form `git:` override silent, because its `path:` is a
sub-directory one level deeper. The flow form has no depth to see, so the regex
searched the whole value, found the `path:` inside `{git: {..., path: sub}}` and
reported a valid file as stale, failing the gate. That is the block/flow
asymmetry the previous round had just fixed for trailing comments, reappearing
on another key. AGENTS.md teaches the flow form, so it is the shape a developer
here is most likely to write.
Patching stopped. The parser moved out of the heredoc into bin/parse-overrides.py
so it can be tested, and bin/parse-overrides-test.py is a table of 25 shapes run
as a bin/check job. Four rounds found four shapes, each fix keyed to the shapes
somebody had thought of and each one finding the next, because hand-exercising
was the only instrument. A fifth shape costs one row now.
The table earns its place: reinstating the naive flow search turns it red on
"git override, flow form" with exactly the reported false positive. Both go
green again on the fix.
Two behaviour changes beyond that. A flow mapping is split on its TOP-LEVEL
commas, so a nested map's keys are never read as the entry's own. And
`dependency_overrides: {magic: {path: /nope}}`, the whole block inline, used to
pass silently; it is parsed rather than made a hard failure, because it is valid
YAML and refusing a valid file is the mistake the quoted-key case already taught.
|
Both fixed, and you found the mirror of the bug the previous round fixed: the block/flow asymmetry I had just closed for trailing comments, reappearing on I stopped patching shapes. You have named the test gap four times and been right four times, and every fix I wrote was keyed to the shapes somebody had thought of, which is exactly why each one found the next. So the parser moved out of the heredoc into The table earns its place rather than decorating the PR. Reinstating the naive flow search turns it red on exactly what you reported: and green again on the fix, 25/25. The two fixes. A flow mapping is split on its TOP-LEVEL commas now, so a nested map's keys can never be read as the entry's own; The
|
|
Note Kodizm (AI-generated). May contain mistakes; verify before acting. Incremental re-review of Since the last round: the heredoc moved to Minor
An unlikely shape in a hand-written overrides file, and much narrower than the four it replaces, but it is the same silent-pass class the module docstring says it guards - one more row in the table, or pushing the entry when the scalar is an anchor/tag, would cover it.
Tests
Checks I ran
|
…d branches on Fifth review round, down to two narrow ones. A package entry whose value is a YAML anchor or tag (`magic: &m` with the block child on the next lines) was never pushed onto the stack, so the child's `path:` was swallowed and a stale path passed silently without reaching the exit-3 branch. An anchor is not the entry's value, it decorates what follows, so the entry is pushed now. Both rows go red on the previous behaviour with the exact reported symptom. The table also stopped at scan(), which left the loud half hand-verified, and the loud half is the point: bin/check branches on the exit code, and 2 and 3 are what turn a bad file into a failure rather than a silent pass. Four exit-code checks run main() through a temporary file for each. AGENTS.md's list of what bin/check runs did not name the overrides-parser job, and the .github mirror carried the same omission because AGENTS.md itself had not changed. Both name it now.
|
Both fixed, and the second one is the more interesting note. The anchor. Reproduced exactly as you had it, then covered by two rows ( An anchor or tag is not the entry's value, it decorates the block child that follows, so the entry is pushed onto the stack rather than treated as a scalar. The exit codes, which you flagged as still hand-verified. That was the sharper point, because the exit code is the whole behaviour: Adding them surfaced a small thing in the instrument itself: the summary line still divided by the shape count, so the moment a second family of checks existed it would have reported
Also confirming your two environment checks from my side, since they are easy to get wrong:
|
The mirror of
uptizm#139, per the boilerplate contract: a structural change there (a rule, a skill, the component contract, tooling likebin/check) travels here in the same piece of work. Product code does not.What travels
bin/sync-registry, which generatesdocs/component-registry.mdfrom the component, layout and preview directories, plus theregistryjob inbin/checkthat fails when it is stale. The registry had been hand-maintained and drifted, which is worse than not having one, because the file is trusted.bin/sync-skills, which copies themagic-frameworkandwind-uiskills into.github/skills/so a reviewer with only this checkout has them, each carrying the hash CI checks it against.bin/checkrefuses a run whose dependency resolution would silently measure pub.dev instead of the local checkouts.PostToolUsehook, rather than at the end of a run.Verification
bin/checkgreen on both halves in this worktree: flutter-analyze, design-tokens, registry, backend-pint, flutter-test, backend-test.